/*
 * Copyright (C) 2012 Richard Schilling
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package dalvik.vm.analysis;

import java.util.EnumSet;

/**
 * Enumeration for register type values. The "hi" piece of a 64-bit value MUST
 * immediately follow the "lo" piece in the enumeration, so we can check that
 * hi==lo+1.
 * <p>
 * Assignment of constants:
 * 
 * <pre>
 *   [-MAXINT,-32768)   : integer
 *   [-32768,-128)      : short
 *   [-128,0)           : byte
 *   0                  : zero
 *   1                  : one
 *   [2,128)            : posbyte
 *   [128,32768)        : posshort
 *   [32768,65536)      : char
 *   [65536,MAXINT]     : integer
 * </pre>
 * <p>
 * Allowed "implicit" widening conversions:
 * 
 * <pre>
 *   zero -> boolean, posbyte, byte, posshort, short, char, integer, ref (null)
 *   one -> boolean, posbyte, byte, posshort, short, char, integer
 *   boolean -> posbyte, byte, posshort, short, char, integer
 *   posbyte -> posshort, short, integer, char
 *   byte -> short, integer
 *   posshort -> integer, char
 *   short -> integer
 *   char -> integer
 * </pre>
 * <p>
 * In addition, all of the above can convert to "float".
 * <p>
 * We're more careful with integer values than the spec requires. The motivation
 * is to restrict byte/char/short to the correct range of values. For example,
 * if a method takes a byte argument, we don't want to allow the code to load
 * the constant "1024" and pass it in.
 */
public enum RegisterType {
    kRegTypeUnknown(0, '_'), /* initial state; use value=0 so calloc works */
    kRegTypeUninit(1, 'U'), /* MUST be odd to distinguish from pointer */
    kRegTypeConflict(2, 'X'), /*
                               * merge clash makes this reg's type unknowable
                               */

    /**
     * Category-1nr types. The order of these is chiseled into a couple of
     * tables, so don't add, remove, or reorder if you can avoid it.
     */
    kRegTypeZero(3, '0'), /* 32-bit 0, could be Boolean, Int, Float, or Ref */
    kRegTypeOne(4, '1'), /* 32-bit 1, could be Boolean, Int, Float */
    kRegTypeBoolean(5, 'Z'), /* must be 0 or 1 */
    kRegTypeConstPosByte(6, 'y'), /* const derived byte, known positive */
    kRegTypeConstByte(7, 'Y'), /* const derived byte */
    kRegTypeConstPosShort(8, 'h'), /* const derived short, known positive */
    kRegTypeConstShort(9, 'H'), /* const derived short */
    kRegTypeConstChar(10, 'c'), /* const derived char */
    kRegTypeConstInteger(11, 'i'), /* const derived integer */
    kRegTypePosByte(12, 'b'), /* byte, known positive (can become char) */
    kRegTypeByte(13, 'B'),
    kRegTypePosShort(14, 's'), /*
                                * short, known positive
                                * (can become char)
                                */
    kRegTypeShort(15, 'S'),
    kRegTypeChar(16, 'C'),
    kRegTypeInteger(17, 'I'),
    kRegTypeFloat(18, 'F'),

    kRegTypeConstLo(19, 'N'), /* const derived wide, lower half */
    kRegTypeConstHi(20, 'n'), /* const derived wide, upper half */
    kRegTypeLongLo(21, 'J'), /* lower-numbered register; endian-independent */
    kRegTypeLongHi(22, 'j'),
    kRegTypeDoubleLo(23, 'D'),
    kRegTypeDoubleHi(24, 'd'),

    /**
     * Enumeration max; this is used with "full" (32-bit) RegType values.
     * Anything larger than this is a ClassObject or uninit ref. Mask off all
     * but the low 8 bits; if you're left with kRegTypeUninit, pull the uninit
     * index out of the high 24. Because kRegTypeUninit has an odd value, there
     * is no risk of a particular ClassObject pointer bit pattern being confused
     * for it (assuming our class object allocator uses word alignment).
     */
    kRegTypeMAX(25, '.');

    private RegisterType(int order, char val) {
        this.order = order;
        this.val = val;
    }

    public int order() {
        return order;
    }

    public int asChar() {
        return val;
    }

    public int asInt() {
        return order;
    }

    private int order;
    private char val;

    public RegisterType lookup(char c) {
        EnumSet<RegisterType> set = EnumSet.allOf(RegisterType.class);

        for (RegisterType rt : set) {
            if (rt.asChar() == c)
                return rt;
        }

        throw new IllegalArgumentException("not found");
    }

    public RegisterType lookup(int i) {
        EnumSet<RegisterType> set = EnumSet.allOf(RegisterType.class);

        for (RegisterType rt : set) {
            if (rt.asInt() == i)
                return rt;
        }

        throw new IllegalArgumentException("not found");
    }

    /**
     * Convert a const derived RegType to the equivalent non-const RegType
     * value.
     * Returns the value passed to the function if the type cannot be derived.
     */
    public static RegisterType constTypeToRegType(RegisterType constType) {

        switch (constType) {
            case kRegTypeConstPosByte:
                return kRegTypePosByte;
            case kRegTypeConstByte:
                return kRegTypeByte;
            case kRegTypeConstPosShort:
                return kRegTypePosShort;
            case kRegTypeConstShort:
                return kRegTypeShort;
            case kRegTypeConstChar:
                return kRegTypeChar;
            case kRegTypeConstInteger:
                return kRegTypeInteger;
            default: {
                return constType;
            }
        }
    }

    /**
     * Given a 32-bit constant, return the most-restricted RegType enum entry
     * that can hold the value. The types used here indicate the value came
     * from a const instruction, and may not correctly represent the real type
     * of the value. Upon use, a constant derived type is updated with the
     * type from the use, which will be unambiguous.
     */
    static RegisterType determineCat1Const(long value) {
        if (value < -32768)
            return kRegTypeConstInteger;
        else if (value < -128)
            return kRegTypeConstShort;
        else if (value < 0)
            return kRegTypeConstByte;
        else if (value == 0)
            return kRegTypeZero;
        else if (value == 1)
            return kRegTypeOne;
        else if (value < 128)
            return kRegTypeConstPosByte;
        else if (value < 32768)
            return kRegTypeConstPosShort;
        else if (value < 65536)
            return kRegTypeConstChar;
        else
            return kRegTypeConstInteger;
    }

}
